﻿using System.Collections.Generic;
using System.Linq;
using CardPlay.Model.ActionModel;
using CardPlay.Model.CardModel;
using CardPlay.Model.GameModel;
using CardPlay.Model.GameModel.Actions;

namespace CardPlay.DemoPack.Games.PairingVariants
{
    [Game("9975E5A3-373A-4850-A4E7-29421E7A0163", LevelOfDifficulty.Easy, LevelOfSkill.MostlyChance, DurationOfPlay.LessThan5Minutes, NumberOfDecks = 2, Family = FamilyName)]
    public class Reversi : SpatialPairingStyleGame
    {
        public Reversi()
        {
            TotalDecks = 2;
            TableauColumns = 7;
            TableauRows = 3;
        }

        protected override bool IsOver
        {
            get { return this[Stock].IsEmpty && !DiscardablePairs.Any(); }
        }

        protected override void PrepareDealScript(DealScript script)
        {
            for (var row = 0; row < TableauRows; ++row)
                for (var col = 0; col < TableauColumns; ++col)
                    script.Deal(col % 2 == 0 ? "up" : "down").To(TableauNameAt(col, row));

            script.DealRemainingFaceDownTo(Stock);
        }

        protected override void OverridePlayingRules(PlayingRules playingRules)
        {
            playingRules.Revert<FlipCardAction>();
        }

        protected override IAction CreateDealAction()
        {
            if (this[Stock].IsEmpty) return null;

            var location = this[Stock].Top;

            var action = new ComplexAction();

            foreach (var targetPile in PilesNamed(AnyTableau).Where(pile => !pile.IsEmpty && pile.TopCard.IsFaceUp).TakeWhile(_ => location.Index >= 0))
            {
                action.Add(new FlipCardAction(this, location));
                action.Add(new MoveCardAction(this, location, new PartLocation(targetPile.Name, targetPile.Count)));

                --location.Index;
            }

            return action;
        }

        protected override IAction CreateRemoveSelectionAction()
        {
            var smallestLocation = Selection.Aggregate(new PartLocation(TableauNameAt(0, TableauRows)), (a, b) => a.GetOrdinal() < b.GetOrdinal() ? a : b);

            var origin = PointFromTableauLocation(smallestLocation);
            var pilesInRow = Enumerable.Range(0, TableauColumns).Select(n => this[TableauNameAt(n, origin.Y)]).ToArray();

            if (origin.X == 0 && pilesInRow[3].IsEmpty)
                return new ComplexAction(pilesInRow.Skip(origin.X).Take(3).Select(pile => new RemovePileAction(this, pile.Bottom)));

            ++origin.X;
            var action = new ComplexAction(pilesInRow.Skip(origin.X).Take(2).Select(pile => new RemovePileAction(this, pile.Bottom)));

            for (var col = origin.X + 2; col < TableauColumns; ++col)
            {
                if (pilesInRow[col].IsEmpty) break;
                action.Add(new MovePileAction(this, pilesInRow[col].Bottom, pilesInRow[col - 2].Bottom));
            }

            return action;
        }

        protected override IEnumerable<PartLocation[]> DiscardablePairs
        {
            get
            {
                for (var row = 0; row < TableauRows; ++row)
                {
                    var previousPile = PileAt(new PartLocation(TableauNameAt(0, row)));

                    for (var col = 2; col < TableauColumns; col += 2)
                    {
                        var pile = PileAt(new PartLocation(TableauNameAt(col, row)));

                        if (!previousPile.IsEmpty && !pile.IsEmpty && previousPile.TopCard.Rank == pile.TopCard.Rank)
                            yield return new[] {previousPile.Top, pile.Top};

                        previousPile = pile;
                    }
                }
            }
        }
    }
}